package com.gonsin.gview.logic.delegate;

import com.gonsin.base.GPageRequest;
import com.gonsin.gview.annotations.actions.GSession;
import com.gonsin.gview.annotations.interfaces.IDownloadActionListener;
import com.gonsin.gview.annotations.model.DownloadRequest;
import com.gonsin.gview.annotations.model.DownloadResponse;
import com.gonsin.gview.logic.exceptions.BaseGViewException;
import com.gonsin.gview.logic.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.springframework.util.NumberUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 代理，实现确认事件
 */
@Slf4j
public class DelegateDownloadListenerImpl implements IDownloadActionListener {

    /**
     * 调用方法的对象
     */
    private Object delegateService;

    /**
     * 需要调用的方法
     */
    private Method actionMethod;

//    private static Type[] PARAMS = {
////            GContext.class,
////            String.class,
//            HttpServletRequest.class,
//            new TypeToken<Map<String, Object>>(){}.getType(),
//            HttpServletResponse.class,
//            String.class,
//            new TypeToken<Map<String, Object>>(){}.getType(),
//            HttpSession.class,
//    };

    public DelegateDownloadListenerImpl(Object delegateService, Method actionMethod) {
        // 检查方法是否有效
//        int count = actionMethod.getParameterCount();
//        actionMethod.getParameterTypes();
//        for(int i = 0; i < count; i++){
//            Type p = actionMethod.getGenericParameterTypes()[i];
//            if(!TypeUtils.isSupport(p, PARAMS)){
////                throw new ActionParameterNotSupport(target, actionMethod, p);
//                throw new BaseGViewException(String.format("类%s，方法名%s，参数类型不支持%s", target.getClass().getName(),
//                        actionMethod.getName(), p.getTypeName()));
//            }
//        }

        if (!Modifier.isPublic(actionMethod.getDeclaringClass().getModifiers())) {
            actionMethod.setAccessible(true);
        }
        this.actionMethod = actionMethod;
        this.delegateService = delegateService;

    }

    @Override
    public DownloadResponse getDownloadUrl(HttpServletRequest request, HttpServletResponse response, DownloadRequest downloadRequest) throws Throwable {
        int count = actionMethod.getParameterCount();
        Object[] paramsArray = new Object[count];

        Map<String, Object> props = downloadRequest.getProps();

//        Map<String, String[]> propBefore = request.getParameterMap();
//        Map<String, String> props = new HashMap<>();
//        propBefore.forEach((k, v) -> {
//            String value = v == null ? null : (v.length > 0 ? v[0] : null);
//            props.put(k, value);
//        });


        actionMethod.getParameterTypes();
        for(int i = 0; i < count; i++){
            Class p = actionMethod.getParameterTypes()[i];
            if(HttpSession.class.isAssignableFrom(p)){
                paramsArray[i] = request.getSession();
                continue;
            }

            if(HttpServletResponse.class.isAssignableFrom(p)){
                paramsArray[i] = response;
                continue;
            }

            if(HttpServletRequest.class.isAssignableFrom(p)){
                paramsArray[i] = request;
                continue;
            }

            if(DownloadRequest.class.isAssignableFrom(p)){
                paramsArray[i] = downloadRequest;
                continue;
            }

            if(GPageRequest.class.isAssignableFrom(p)){
                GPageRequest pageRequest = new GPageRequest();
                pageRequest.setPage(NumberUtils.parseNumber(Objects.toString(props.get("page")), Integer.class));
                pageRequest.setCount(NumberUtils.parseNumber(Objects.toString(props.get("count")), Integer.class));
                if(props.containsKey("sort")){
                    pageRequest.setSort(Objects.toString(props.get("sort")));
                }
                paramsArray[i] = pageRequest;
                continue;
            }

            // 如果是基本数据类型
            if(ValueUtils.isWrapClass(p)){

                GSession gSession = ValueUtils.getSessionAnnotation(actionMethod, i);
                if(gSession != null){
                    String sessionName = gSession.value();
                    Object value = ValueUtils.parseValue(request.getSession().getAttribute(sessionName), p);
                    paramsArray[i] = value;
                    if(value == null){
                        log.error("获取到session内【{}】的值为空，请检查在【{}.{}】中session是否书写正确", sessionName, delegateService.getClass().getSimpleName(), actionMethod.getName());
                    }
                } else {
                    String name = ValueUtils.getParamName(actionMethod, i);
                    Object value = ValueUtils.parseValue(props.get(name), p);
                    paramsArray[i] = value;
                    if(value == null){
                        log.error("获取到【{}】的值为空，请查看是否在【{}.{}】中忘记填入@GSession标注或者@GParam标注", name, delegateService.getClass().getSimpleName(), actionMethod.getName());
                    }
                }
                continue;
            }


            // 创建类转存表单
            Object searchPo = null;
            try {
                searchPo = Class.forName(p.getTypeName()).newInstance();
            } catch (Exception e) {
                log.error(e.toString(), e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getName()));
            }
            Map<String, Object> searchPoMap = new HashMap<>();
            Field[] allFields = p.getDeclaredFields();
            for(Field field : allFields){
                try {
                    searchPoMap.put(field.getName(), ValueUtils.parseValue(props.get(field.getName()), field.getGenericType()));
                } catch (Exception ex) {
                    log.warn(ex.toString(), ex);
                    log.warn("设置参数【{}】，设置的内容为“{}“", field.getName(), props.get(field.getName()));
                }
            }
            try {
                BeanUtilsBean utils = BeanUtilsBean.getInstance();
                utils.getConvertUtils().register(new MyDateConverter(), java.util.Date.class);
                utils.getConvertUtils().register(new MyBooleanConverter(), Boolean.class);
                utils.getConvertUtils().register(new MyIntegerConverter(), Integer.class);
                utils.getConvertUtils().register(new MyLongConverter(), Long.class);
                utils.getConvertUtils().register(new MyShortConverter(), Short.class);
                utils.getConvertUtils().register(new MyByteConverter(), Byte.class);
                utils.getConvertUtils().register(new MyDoubleConverter(), Double.class);
                utils.getConvertUtils().register(new MyFloatConverter(), Float.class);
                utils.getConvertUtils().register(new MyCharConverter(), Character.class);
                utils.getConvertUtils().register(new MyListConverter(), List.class);
                utils.getConvertUtils().register(new MySetConverter(), Set.class);
                utils.populate(searchPo, searchPoMap);
                paramsArray[i] = searchPo;
                continue;
            } catch (Exception e) {
                log.error(e.toString() ,e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getTypeName()));
            }
        }

        try {
            DownloadResponse url = (DownloadResponse) actionMethod.invoke(delegateService, paramsArray);
            return url;
        } catch (Exception e) {
            if(e instanceof InvocationTargetException){
                throw ((InvocationTargetException) e).getTargetException();
            }
            throw e;
        }
    }
}
